#include "ExampleAIModule.h"
using namespace BWAPI;

bool rush = false; //for activating zealot & goon rush.
bool photonOver = false; //until photonRush is over. True = no photon rush, false = photon rush!
bool aiInitiated = false; //until regular AI is enabled.
int aiStart = 0; //for measuring when PhotonRush tactic ends, and regular AI starts.

void ExampleAIModule::onStart()
{
	Broodwar->printf("The map is %s, a %d player map",Broodwar->mapName().c_str(),Broodwar->getStartLocations().size());
	// Enable some cheat flags
	Broodwar->enableFlag(Flag::UserInput);
	Broodwar->setLocalSpeed(0);
	// Uncomment to enable complete map information
	//Broodwar->enableFlag(Flag::CompleteMapInformation);

	//read map information into BWTA so terrain analysis can be done in another thread
	BWTA::readMap();
	analyzed=false;
	analysis_just_finished=false;

	this->enhancedUI = new EnhancedUI();

	if (Broodwar->isReplay())
	{
		Broodwar->printf("The following players are in this replay:");
		for(std::set<Player*>::iterator p=Broodwar->getPlayers().begin();p!=Broodwar->getPlayers().end();p++)
		{
			if (!(*p)->getUnits().empty() && !(*p)->isNeutral())
			{
				Broodwar->printf("  %s, playing as a %s",(*p)->getName().c_str(),(*p)->getRace().getName().c_str());
			}
		}
	}
	else
	{
		//Run analyze thread
		Broodwar->printf("Analyzing map... this may take a minute");
		CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)AnalyzeThread, NULL, 0, NULL);
	}
}
void ExampleAIModule::onEnd(bool isWinner)
{
	if (isWinner)
	{
		Broodwar->sendText("I win! I win! :P");
	}
	Broodwar->sendText("Good game :)");
}
void ExampleAIModule::onFrame()
{
	if (Broodwar->isReplay())
		return;
	while (!analyzed){}
	//First initate only what is needed for the photon rush
	if (analysis_just_finished)
	{
		Broodwar->printf("Finished analyzing map!");
		analysis_just_finished = false;

		//Init BWSAL system
		this->enhancedUI->setArbitrator(&this->arbitrator);
		this->buildManager       = new BuildManager(&this->arbitrator);
		this->techManager        = new TechManager(&this->arbitrator);
		this->upgradeManager     = new UpgradeManager(&this->arbitrator);
		this->scoutManager       = new ScoutManager(&this->arbitrator);
		this->workerManager      = new WorkerManager(&this->arbitrator);
		this->buildOrderManager  = new BuildOrderManager(this->buildManager,this->techManager,this->upgradeManager,this->workerManager);
		this->baseManager        = new BaseManager();
		this->supplyManager      = new SupplyManager();
		this->borderManager      = new BorderManager();
		this->defenseManager     = new DefenseManager(&this->arbitrator);
		this->informationManager = new InformationManager();
		this->unitGroupManager   = new UnitGroupManager();

		this->harassmentManager	 = new HarassmentManager(&this->arbitrator);

		this->supplyManager->setBuildManager(this->buildManager);
		this->supplyManager->setBuildOrderManager(this->buildOrderManager);
		this->techManager->setBuildingPlacer(this->buildManager->getBuildingPlacer());
		this->upgradeManager->setBuildingPlacer(this->buildManager->getBuildingPlacer());
		this->workerManager->setBaseManager(this->baseManager);
		this->workerManager->setBuildOrderManager(this->buildOrderManager);
		this->baseManager->setBuildOrderManager(this->buildOrderManager);

		this->borderManager->setInformationManager(this->informationManager);
		this->borderManager->setBaseManager(this->baseManager);
		this->defenseManager->setBorderManager(this->borderManager);

		this->buildOrderManager->enableDependencyResolver();
	}

	//when rush is complete or aborted, continue normally
	if (!aiInitiated && analyzed){ //photonOver && 
		aiInitiated = true;

		BWAPI::Race race = Broodwar->self()->getRace();
		BWAPI::Race enemyRace = Broodwar->enemy()->getRace();
		BWAPI::UnitType workerType=*(race.getWorker());
		double minDist;
		BWTA::BaseLocation* natural=NULL;
		BWTA::BaseLocation* home=BWTA::getStartLocation(Broodwar->self());
		for(std::set<BWTA::BaseLocation*>::const_iterator b=BWTA::getBaseLocations().begin();b!=BWTA::getBaseLocations().end();b++)
		{
			if (*b == home) continue;
			double dist=home->getGroundDistance(*b);
			if (dist>0)
			{
				if (natural==NULL || dist<minDist)
				{
					minDist=dist;
					natural=*b;
				}
			}
		}

		//make the basic production facility (initial build queue)
		if (race == Races::Zerg)
		{
			//send an overlord out if Zerg
			this->scoutManager->setScoutCount(1);

			//12 hatch
			this->buildOrderManager->build(12,workerType,80);
			this->baseManager->expand(natural,79);
			this->buildOrderManager->build(20,workerType,78);
			this->buildOrderManager->buildAdditional(1,UnitTypes::Zerg_Spawning_Pool,60);
			this->buildOrderManager->buildAdditional(100,UnitTypes::Zerg_Zergling,82);
		}
		else if (race == Races::Terran)
		{
			this->buildOrderManager->build(20,workerType,80);
			if (enemyRace == Races::Zerg)
			{
				this->buildOrderManager->buildAdditional(1,UnitTypes::Terran_Barracks,60);
				this->buildOrderManager->buildAdditional(9,UnitTypes::Terran_Marine,45);
				this->buildOrderManager->buildAdditional(1,UnitTypes::Terran_Refinery,42);
				this->buildOrderManager->buildAdditional(1,UnitTypes::Terran_Barracks,40);
				this->buildOrderManager->buildAdditional(1,UnitTypes::Terran_Academy,39);
				this->buildOrderManager->buildAdditional(9,UnitTypes::Terran_Medic,38);
				this->buildOrderManager->research(TechTypes::Stim_Packs,35);
				this->buildOrderManager->research(TechTypes::Tank_Siege_Mode,35);
				this->buildOrderManager->buildAdditional(3,UnitTypes::Terran_Siege_Tank_Tank_Mode,34);
				this->buildOrderManager->buildAdditional(2,UnitTypes::Terran_Science_Vessel,30);
				this->buildOrderManager->research(TechTypes::Irradiate,30);
				this->buildOrderManager->upgrade(1,UpgradeTypes::Terran_Infantry_Weapons,20);
				this->buildOrderManager->build(3,UnitTypes::Terran_Missile_Turret,13);
				this->buildOrderManager->upgrade(3,UpgradeTypes::Terran_Infantry_Weapons,12);
				this->buildOrderManager->upgrade(3,UpgradeTypes::Terran_Infantry_Armor,12);
				this->buildOrderManager->build(1,UnitTypes::Terran_Engineering_Bay,11);
				this->buildOrderManager->buildAdditional(40,UnitTypes::Terran_Marine,10);
				this->buildOrderManager->build(6,UnitTypes::Terran_Barracks,8);
				this->buildOrderManager->build(2,UnitTypes::Terran_Engineering_Bay,7);
				this->buildOrderManager->buildAdditional(10,UnitTypes::Terran_Siege_Tank_Tank_Mode,5);
			}
			else
			{
				this->buildOrderManager->buildAdditional(2,BWAPI::UnitTypes::Terran_Machine_Shop,70);
				this->buildOrderManager->buildAdditional(3,BWAPI::UnitTypes::Terran_Factory,60);
				this->buildOrderManager->research(TechTypes::Spider_Mines,55);
				this->buildOrderManager->research(TechTypes::Tank_Siege_Mode,55);
				this->buildOrderManager->buildAdditional(20,BWAPI::UnitTypes::Terran_Vulture,40);
				this->buildOrderManager->buildAdditional(20,BWAPI::UnitTypes::Terran_Siege_Tank_Tank_Mode,40);
				this->buildOrderManager->upgrade(3,UpgradeTypes::Terran_Vehicle_Weapons,20);
			}
		}
		else if (race == Races::Protoss)
		{
			//NOTE: Higher number -> higher priority
			//this->buildOrderManager->buildAdditional(15,UnitTypes::Protoss_Probe,100);
			//this->buildOrderManager->buildAdditional(1,UnitTypes::Protoss_Pylon,99);
			//this->buildOrderManager->build(1,UnitTypes::Protoss_Forge,98);
			//this->buildOrderManager->build(2,UnitTypes::Protoss_Photon_Cannon,96);
			//this->buildOrderManager->buildAdditional(3,UnitTypes::Protoss_Zealot,95);
			//this->buildOrderManager->buildAdditional(1,UnitTypes::Protoss_Dragoon,67);			
			//this->buildOrderManager->upgrade(1,UpgradeTypes::Protoss_Plasma_Shields,66);
			//this->buildOrderManager->upgrade(1,UpgradeTypes::Singularity_Charge,65);
			//this->buildOrderManager->upgrade(1,UpgradeTypes::Protoss_Armor,65);
			//this->buildOrderManager->upgrade(1,UpgradeTypes::Protoss_Ground_Weapons,64);

			////NOTE: Higher number -> higher priority
			this->buildOrderManager->build(20,workerType,80);
			this->buildOrderManager->build(3,UnitTypes::Protoss_Pylon,78);
			this->buildOrderManager->build(3,UnitTypes::Protoss_Photon_Cannon,75);
			this->buildOrderManager->build(2,UnitTypes::Protoss_Gateway,73);
			this->buildOrderManager->build(10,UnitTypes::Protoss_Zealot,72);
			this->buildOrderManager->buildAdditional(2,UnitTypes::Protoss_Gateway,70);
			this->buildOrderManager->buildAdditional(3,UnitTypes::Protoss_Photon_Cannon,70);
			this->buildOrderManager->build(20,UnitTypes::Protoss_Dragoon,70);
			//this->baseManager->expand(71);
			this->buildOrderManager->buildAdditional(10,UnitTypes::Protoss_Zealot,50);
			this->buildOrderManager->buildAdditional(20,UnitTypes::Protoss_Dragoon,48);
			this->buildOrderManager->upgrade(1,UpgradeTypes::Leg_Enhancements,41);
			this->buildOrderManager->upgrade(1,UpgradeTypes::Protoss_Ground_Weapons,41);
			this->buildOrderManager->upgrade(1,UpgradeTypes::Singularity_Charge,41);
			this->buildOrderManager->upgrade(2,UpgradeTypes::Protoss_Plasma_Shields,30);
			this->buildOrderManager->upgrade(1,UpgradeTypes::Protoss_Armor,30);
		}
		this->workerManager->enableAutoBuild();
		this->workerManager->setAutoBuildPriority(60);
		//this->scoutManager->setScoutCount(1);
		this->enhancedUI->setAnalyzed(true);
	}
	if (analyzed && !photonOver){
		this->harassmentManager->update();
		//this->buildManager->update();
		//this->buildOrderManager->update();
		//this->baseManager->update();
		//this->workerManager->update();		
		//this->arbitrator.update();

		//this->buildOrderManager->build(8,UnitTypes::Protoss_Probe,100);
		if (!this->harassmentManager->isHarassing()){
			//photonOver = true;
			//aiStart = Broodwar->getFrameCount();
		}
	}

	if (analyzed && aiInitiated)
	{
		this->buildManager->update();
		this->buildOrderManager->update();
		this->baseManager->update();
		this->workerManager->update();
		this->techManager->update();
		this->upgradeManager->update();
		this->supplyManager->update();
		this->scoutManager->update();
		this->defenseManager->update();
		this->borderManager->update();
		this->arbitrator.update();


		//HOLD THE CHOKEPOINT
		/*if (true){
		std::set<BWTA::Chokepoint*> chokepoints= home->getChokepoints();
		double max_length=0;
		BWTA::Chokepoint* choke=NULL;
		for(std::set<BWTA::Chokepoint*>::iterator c=chokepoints.begin();c!=chokepoints.end();c++)
		{
		double length=(*c)->getWidth();
		if (length>max_length || choke==NULL)
		{
		max_length=length;
		choke=*c;
		}					
		}
		this->buildOrderManager->build(2,UnitTypes::Protoss_Pylon,94, choke->getCenter());
		this->buildOrderManager->build(6,UnitTypes::Protoss_Photon_Cannon,94, choke->getCenter());
		}	*/

		////FIRST BUILDING ADD (stupid I know, but with scripted rush managers get confused, and adress all probes to build (but no one to mine..) otherwise.
		//if ((Broodwar->getFrameCount()-aiStart) == 3000){
		//	this->buildOrderManager->build(6,UnitTypes::Protoss_Photon_Cannon,94);
		//	this->buildOrderManager->build(3,UnitTypes::Protoss_Gateway,94);
		//}

		////SUPPLY ADDONS
		//if (Broodwar->self()->supplyUsed() > Broodwar->self()->supplyTotal()-20)
		//{
		//	int pylons_needed = Broodwar->self()->supplyTotal()/16;
		//	this->buildOrderManager->build(pylons_needed,UnitTypes::Protoss_Pylon,50);
		//}


		//EXPAND
		if (((Broodwar->getFrameCount()-aiStart) == 8200) ||
			((Broodwar->getFrameCount()-aiStart) == 13000) || 
			((Broodwar->getFrameCount()-aiStart) == 20000)){
				this->baseManager->expand();
				//this->buildOrderManager->build(1,UnitTypes::Protoss_Assimilator,72);
		}

		//chain of increase
		//if ((Broodwar->getFrameCount()-aiStart) % 200 == 0 && Broodwar->self()->supplyUsed() < 380){
		//	this->buildOrderManager->buildAdditional(2, UnitTypes::Protoss_Zealot, 90);
		//}
		//else if ((Broodwar->getFrameCount()-aiStart) % 200 == 0 && (Broodwar->getFrameCount()-aiStart) > 10000 && Broodwar->self()->supplyUsed() < 380){
		//	this->buildOrderManager->buildAdditional(1, UnitTypes::Protoss_Zealot, 90);
		//	this->buildOrderManager->buildAdditional(3, UnitTypes::Protoss_Dragoon, 90);
		//}
		//else if ((Broodwar->getFrameCount()-aiStart) % 200 == 0 && (Broodwar->getFrameCount()-aiStart) > 20000 && Broodwar->self()->supplyUsed() < 380){
		//	this->scoutManager->setScoutCount(3);
		//	this->buildOrderManager->buildAdditional(1, UnitTypes::Protoss_Zealot, 90);
		//	this->buildOrderManager->buildAdditional(3, UnitTypes::Protoss_Dragoon, 90);
		//	this->buildOrderManager->buildAdditional(1,UnitTypes::Protoss_Gateway,100);
		//}
		//else if ((Broodwar->getFrameCount()-aiStart) % 200 == 0 && (Broodwar->getFrameCount()-aiStart) > 30000 && Broodwar->self()->supplyUsed() < 380){
		//	this->scoutManager->setScoutCount(5);
		//	this->buildOrderManager->buildAdditional(3, UnitTypes::Protoss_Zealot, 90);
		//	this->buildOrderManager->buildAdditional(5, UnitTypes::Protoss_Dragoon, 90);
		//	this->buildOrderManager->buildAdditional(1,UnitTypes::Protoss_Gateway,100);
		//}
		//if ((Broodwar->getFrameCount()-aiStart) > 80000 || Broodwar->self()->supplyUsed() > 380){
		//	this->scoutManager->setScoutCount(12);
		//}


		/*if ((Broodwar->getFrameCount()-aiStart) % 240){
		std::set<BWTA::Chokepoint*> chokepoints= BWTA::getBaseLocations -> getChokepoints();
		BWTA::Chokepoint* choke=NULL;
		for(std::set<BWTA::Chokepoint*>::iterator c=chokepoints.begin();c!=chokepoints.end();c++)
		{
		choke=*c;
		this->buildOrderManager->build(2,UnitTypes::Protoss_Pylon,70, choke->getCenter());
		this->buildOrderManager->build(20,UnitTypes::Protoss_Photon_Cannon,70, choke->getCenter());
		}
		}
		*/

		/*if ((Broodwar->getFrameCount()-aiStart) % 242){
		std::set<BWTA::Chokepoint*> chokepoints= BWTA::getChokepoints();
		BWTA::Chokepoint* choke=NULL;
		for(std::set<BWTA::Chokepoint*>::iterator c=chokepoints.begin();c!=chokepoints.end();c++)
		{
		choke=*c;
		this->buildOrderManager->build(2,UnitTypes::Protoss_Pylon,95, choke->getCenter());
		this->buildOrderManager->build(6,UnitTypes::Protoss_Photon_Cannon,95, choke->getCenter());
		}
		}*/

		/*if ((Broodwar->getFrameCount()-aiStart) % 400 && (Broodwar->getFrameCount()-aiStart) > 12000){

		for(std::set<Unit*>::const_iterator i = Broodwar->self()->getUnits().begin(); i != Broodwar->self()->getUnits().end(); i++)
		{
		if ((*i)->getType().getID() == 154 && ((*i)->getPosition() != (*i)->getPlayer()->getStartLocation())){ //=addedNexus
		this->buildOrderManager->build(2,UnitTypes::Protoss_Pylon,93, (*i)->getPosition());
		this->buildOrderManager->build(20,UnitTypes::Protoss_Photon_Cannon,93, (*i)->getPosition());
		}
		}
		}*/
	}
	this->enhancedUI->update();

	std::set<Unit*> enemyUnits;
	for(std::set<Unit*>::iterator m = Broodwar->getAllUnits().begin();m != Broodwar->getAllUnits().end(); ++m)
	{
		if ((*m)->getPlayer()->isEnemy(Broodwar->self()))
		{
			enemyUnits.insert((*m));
		}
	}

	//if (Broodwar->getSelectedUnits().begin() != Broodwar->getSelectedUnits().end())
	//      Broodwar->drawTextScreen(5,16,"Coolddown: (%i) (%i)", (*Broodwar->getSelectedUnits().begin())->getGroundWeaponCooldown(), (*Broodwar->getSelectedUnits().begin())->getAirWeaponCooldown());
	//if ((Broodwar->getFrameCount()-aiStart) % 10 == 0)
	//      getDragoons()->setCenter(getDragoons()->getCenter() + BWAPI::Position(1, 0));

	std::set<CFormation*> formations = CFormation::getFormations();
	for (std::set<CFormation*>::iterator i = formations.begin(); i != formations.end(); ++i)
	{
		if ((Broodwar->getFrameCount()-aiStart)%30 == 0 && !(*i)->isWellFormed())
			(*i)->recalculateSlots();
		for(std::set<Unit*>::iterator m = enemyUnits.begin();m != enemyUnits.end(); ++m)
		{
			if ((*i)->getTarget() == 0 || ((*m)->getDistance((*i)->getCenter()) < (*i)->getTarget()->getDistance((*i)->getCenter())))
				if ((*m)->getDistance((*i)->getCenter()) < 1500)
					(*i)->setTarget(*m);
		}
	}

	//The rush initiator logic counts the dragoons and zealots in the beginning of the big for-loop, and
	//analyzes the results after the loop.
	int dragoons = 0;
	int zealots = 0;

	for(std::set<Unit*>::const_iterator i = Broodwar->self()->getUnits().begin(); i != Broodwar->self()->getUnits().end(); i++)
	{
		Position pos=(*i)->getPosition();
		CFormation *group = CFormation::getFormation((*i));

		//CANCEL CONSTRUCTION
		//if ((*i)->isBeingConstructed() && (((*i)->getType().maxShields() > 0 && (*i)->getShields() == 0) || (*i)->getHitPoints() < (*i)->getInitialHitPoints()))
		//	(*i)->cancelConstruction();
		if (!enemyUnits.empty()){
			for(std::set<Unit*>::const_iterator j = enemyUnits.begin(); j != enemyUnits.end(); j++){
				if ((*i)->isBeingConstructed() && ((*j)->getTarget() == (*i)) && 
					(((*i)->getType().maxShields() > 0 && (*i)->getShields() == 0) || (*i)->getHitPoints() < (*i)->getInitialHitPoints()))
				{
					(*i)->cancelConstruction();
				}
			}
		}
		if (group)
		{
			if (((*i)->isIdle() || (*i)->isMoving()) && ((*i)->getTarget() == 0) && ((*i)->getPosition() != group->getPosition(*i)))
				(*i)->attackMove(group->getPosition(*i));
		}
		//if (!enemyUnits.empty())
		//{
		//	//Get nearest enemy whose sight range we are in.
		//	Unit* nearest = 0;
		//	double nearestDist = 0;
		//	for(std::set<Unit*>::const_iterator j = enemyUnits.begin(); j != enemyUnits.end(); j++)
		//	{
		//		double dist = (*i)->getDistance(*j);
		//		if (dist <= (*j)->getType().sightRange() && (dist < nearestDist || nearest == 0))
		//		{
		//			nearest = *j;
		//			nearestDist = dist;
		//		}
		//	}
		//	if (nearest && (*i)->isMoving())
		//	{
		//		(*i)->attackUnit(nearest);
		//	}
		//}

		//COUNT UNITS TO DECIDE WHETHER OR NOT TO RUSH
		if ((*i)->getType().getID() == 65){
			zealots++;
		}
		else if ((*i)->getType().getID() == 66){
			dragoons++;
		}

		if (false) //Use Experimental Zealot Attack algorithm
		{
			//ZEALOT ATTACK ROUTINE
			if ((*i)->getType().getID() == 65 && (Broodwar->getFrameCount()-aiStart)%15==0 && (!(*i)->getGroundWeaponCooldown()))
			{
				Unit* target = 0;
				double smallestDeath = 100000;

				if (!enemyUnits.empty() && !(*i)->isStartingAttack()){
					for(std::set<Unit*>::const_iterator j = enemyUnits.begin(); j != enemyUnits.end(); j++){
						double deathCounter = (*j)->getHitPoints() + (*j)->getShields(); //Is negative, if target dies before unit can land a blow. Is small if unit is about to die soon (priority target). (NOW WORKS, DON'T BREAK!)
						int killerCounter = 0; //Calculates how many attackers are currently attacking the could-be-target. (IS NEEDED, UNTIL WE FIGURE OUT, HOW TO FIND OUT IF THERE IS ROOM TO ATTACK)
						double dps = 0;
						for(std::set<Unit*>::const_iterator k=Broodwar->self()->getUnits().begin();k!=Broodwar->self()->getUnits().end();k++){
							if (((*k)->getTarget() == (*j) && (*k)->getDistance(*j) < 50) || ((*k)->getTarget() == (*j) && (*k)->getType().groundWeapon()->maxRange() > 10)){
								dps += ((*k)->getType().groundWeapon()->damageAmount()/(*k)->getType().groundWeapon()->damageCooldown()) * ((*i)->getType().groundWeapon()->damageCooldown());
								if ((*k)->getType().getID() == 65){
									killerCounter++;
								}
							}
						}

						deathCounter -= dps;

						if ((*i)->getDistance(*j) > 60){
							deathCounter += 1000;  //So the unit will priorize close targets
						}

						if (deathCounter < smallestDeath && deathCounter > 0 && killerCounter < 4){
							if (deathCounter > 0){
								smallestDeath = deathCounter;
							}
							target = (*j);
						}
					}

					if (target && ((*i)->getDistance(target) > 22)){ //If the target is not adjacent -> attackMove, so if target is unreachable, a new target is automatically selected.
						(*i)->attackMove(target->getPosition());
					}
					else if (target){
						(*i)->attackUnit(target);
					}
				}
			}
		}
		if (rush) //Use Experimental Dragoon Attack algorithm
		{
			// DRAGOON ATTACK ROUTINE
			// The same, but the distance to enemy is not considered in deathCounter, and the killerCounter is removed.

			if ((*i)->getType().getID() == 66 && (Broodwar->getFrameCount()-aiStart)%8==0 && (((*i)->getType().groundWeapon()->damageCooldown() - (*i)->getGroundWeaponCooldown())< 8 || (!(*i)->getGroundWeaponCooldown())))
			{
				Unit* target = 0;
				double smallestDeath = 100000;

				if (!enemyUnits.empty() && !(*i)->isStartingAttack()){
					for(std::set<Unit*>::const_iterator j = enemyUnits.begin(); j != enemyUnits.end(); j++){
						double deathCounter = (*j)->getHitPoints() + (*j)->getShields();
						double dps = 0;
						for(std::set<Unit*>::const_iterator k=Broodwar->self()->getUnits().begin();k!=Broodwar->self()->getUnits().end();k++){
							if ((*k)->getTarget() == (*j)){
								if (((*k)->getType().getID() == 65) && ((*k)->getType().groundWeapon()->damageCooldown()) > ((*k)->getDistance(*j) / (*k)->getType().topSpeed())){
									dps += ((*k)->getType().groundWeapon()->damageAmount()/(*k)->getType().groundWeapon()->damageCooldown()) * ((*i)->getType().groundWeapon()->damageCooldown());
								}
								if (((*k)->getType().getID() == 66)){
									dps += ((*k)->getType().groundWeapon()->damageAmount()/(*k)->getType().groundWeapon()->damageCooldown()) * ((*i)->getType().groundWeapon()->damageCooldown());																													
								}
							}
						}

						deathCounter -= dps;

						if ((*i)->getDistance(*j) > (*i)->getType().groundWeapon()->maxRange()*6/4 + 40){
							deathCounter += 1000;  //The unit priorizes targets that are in range, or just slightly out of range
						}

						if (deathCounter < smallestDeath && deathCounter > 0){
							if (deathCounter > 0){
								smallestDeath = deathCounter;
							}
							target = (*j);
						}
					}

					if (target){
						(*i)->attackUnit(target);
					}
				}
			}
		}

		if (false) //Use Experimental Dodge algorithm
		{
			//DODGE ROUTINE
			if (!enemyUnits.empty()) // && ((Broodwar->getFrameCount()-aiStart)%30==0))
			{
				int targetedCount = 0; // All targeters; Dodger moves away from the average position of all enemies, who target it.
				int dragoonsTargeting = 0;
				int zealotsTargeting = 0;
				int closestZealotDistance = 1000000;
				int vaistettava_x = 0;
				int vaistettava_y = 0;
				for(std::set<Unit*>::const_iterator j = enemyUnits.begin();j != enemyUnits.end(); j++){
					if ((*j)->getTarget() == (*i) || (*j)->getOrderTarget() == (*i)) {
						targetedCount++;
						if ((*j)->getType().getID() == 65){
							zealotsTargeting++;
							if ((*j)->getDistance(*i) < closestZealotDistance){
								closestZealotDistance = (*j)->getDistance(*i);
							}
						}
						else if ((*j)->getType().getID() == 66){
							dragoonsTargeting++;
						}
						vaistettava_x += (*j)->getPosition().x();
						vaistettava_y += (*j)->getPosition().y();
					}
				}

				if (targetedCount > 0)
				{
					vaistettava_x /= targetedCount;
					vaistettava_y /= targetedCount;
					vaistettava_x = (*i)->getPosition().x() - vaistettava_x;
					vaistettava_y = (*i)->getPosition().y() - vaistettava_y;
					vaistettava_x = ((*i)->getPosition().x() + vaistettava_x/3);
					vaistettava_y = ((*i)->getPosition().y() + vaistettava_y/3);

					if (
						//((*i)->getType().getID() == 65 &&
						//      (zealotsTargeting >= 3)) || //ZEALOTS: If 3 or more zealots targets you
						//DRAGOONS
						((*i)->getType().getID() == 66 && ((*i)->getShields() == 0) && ((*i)->getHitPoints() < 80) &&
						((dragoonsTargeting > 0) || (zealotsTargeting > 0 && closestZealotDistance <= 15)))
						)
					{
						Position dodge = Position(vaistettava_x, vaistettava_y);
						(*i)->rightClick(dodge);
					}
				}
			}
		}
	}

	//THE LOGIC FOR INITIATING RUSH
	//TODO:  This should be a manager (AssaultManager?) so it can outbid the defense manager for the units.
	//			This manager should also use the formations and potentially hunt down the enemy. (Can use information manager)
	//			The algorithms above would probably work fine in it.  You might want to limit the manager to using dragoons.
	//			The biggest problem it has at the moment is it doesn't hunt for the enemy.
	if ((dragoons > 13 && !rush)) { // || ((zealots + dragoons) > 19 && rush && !this->scoutManager->isScouting())){
		rush = true;
		this->scoutManager->setScoutCount(2);
		Broodwar->printf("Rush initiated!!");
	//} else if (zealots > 5 && !rush) {
	//	rush = true;
	//	Broodwar->printf("Squad formed!!");
	} else if ((dragoons < 6) && rush){
		rush = false;
		Broodwar->printf("Regrouping..");
	}
}

void ExampleAIModule::onUnitCreate(BWAPI::Unit* unit)
{
	if (!Broodwar->isReplay())
	{
		Broodwar->printf("A %s [%x] has been created at (%d,%d)",unit->getType().getName().c_str(),unit,unit->getPosition().x(),unit->getPosition().y());
	}
	else
	{
		/*if we are in a replay, then we will print out the build order
		(just of the buildings, not the units).*/
		if (unit->getType().isBuilding() && unit->getPlayer()->isNeutral()==false)
		{
			int seconds=(Broodwar->getFrameCount()-aiStart)/24;
			int minutes=seconds/60;
			seconds%=60;
			Broodwar->printf("%.2d:%.2d: %s creates a %s",minutes,seconds,unit->getPlayer()->getName().c_str(),unit->getType().getName().c_str());
		}
	}
}
void ExampleAIModule::onUnitDestroy(BWAPI::Unit* unit)
{
	if (!Broodwar->isReplay())
	{
		if (unit->getPlayer()->isEnemy(Broodwar->self()))
			Broodwar->printf("\x07 A %s [%x] has been destroyed at (%d,%d)",unit->getType().getName().c_str(),unit,unit->getPosition().x(),unit->getPosition().y());
		else
			Broodwar->printf("\x08 A %s [%x] has been destroyed at (%d,%d)",unit->getType().getName().c_str(),unit,unit->getPosition().x(),unit->getPosition().y());
		if (CFormation *group = CFormation::getFormation(unit))
			group->leaveFormation(unit);

		if (unit->getType().getID() == 154){ //=nexus
			this->baseManager->expand();

		}
		if (analyzed)
		{
			this->arbitrator.onRemoveObject(unit);
			this->buildManager->onRemoveUnit(unit);
			this->techManager->onRemoveUnit(unit);
			this->upgradeManager->onRemoveUnit(unit);
			this->workerManager->onRemoveUnit(unit);
			this->scoutManager->onRemoveUnit(unit);
			this->defenseManager->onRemoveUnit(unit);
			this->informationManager->onUnitDestroy(unit);
			this->harassmentManager->onRemoveUnit(unit);
		}
	}
}

void ExampleAIModule::onUnitMorph(BWAPI::Unit* unit)
{
	if (!Broodwar->isReplay())
	{
		if (unit->getPlayer()->isEnemy(Broodwar->self()))
			Broodwar->printf("\x11 A %s [%x] has been morphed at (%d,%d)",unit->getType().getName().c_str(),unit,unit->getPosition().x(),unit->getPosition().y());
		else
			Broodwar->printf("A %s [%x] has been morphed at (%d,%d)",unit->getType().getName().c_str(),unit,unit->getPosition().x(),unit->getPosition().y());
		if (analyzed)
		{
			this->unitGroupManager->onUnitMorph(unit);
		}
	}
	else
	{
		/*if we are in a replay, then we will print out the build order
		(just of the buildings, not the units).*/
		if (unit->getType().isBuilding() && unit->getPlayer()->isNeutral()==false)
		{
			int seconds=(Broodwar->getFrameCount()-aiStart)/24;
			int minutes=seconds/60;
			seconds%=60;
			Broodwar->printf("%.2d:%.2d: %s morphs a %s",minutes,seconds,unit->getPlayer()->getName().c_str(),unit->getType().getName().c_str());
		}
	}
}
void ExampleAIModule::onUnitShow(BWAPI::Unit* unit)
{
	if (!Broodwar->isReplay())
	{
		if (unit->getPlayer()->isEnemy(Broodwar->self()))
			Broodwar->printf("\x11 A %s [%x] has been spotted at (%d,%d)",unit->getType().getName().c_str(),unit,unit->getPosition().x(),unit->getPosition().y());
		if (analyzed)
		{
			this->informationManager->onUnitShow(unit);
			this->unitGroupManager->onUnitShow(unit);
		}
	}
}
void ExampleAIModule::onUnitHide(BWAPI::Unit* unit)
{
	if (!Broodwar->isReplay())
	{
		if (unit->getPlayer()->isEnemy(Broodwar->self()))
		{
			//Broodwar->printf("\x08 A %s [%x] was last seen at (%d,%d)",unit->getType().getName().c_str(),unit,unit->getPosition().x(),unit->getPosition().y());
		}
		if (analyzed)
		{
			this->informationManager->onUnitHide(unit);
			this->unitGroupManager->onUnitHide(unit);
		}
	}
}

void ExampleAIModule::onUnitRenegade(BWAPI::Unit* unit)
{
	if (!Broodwar->isReplay())
	{
		Broodwar->printf("\x10 A %s [%x] is now owned by %s",unit->getType().getName().c_str(),unit,unit->getPlayer()->getName().c_str());
		if (analyzed)
		{
			this->unitGroupManager->onUnitRenegade(unit);
		}
	}
}
void ExampleAIModule::onPlayerLeft(BWAPI::Player* player)
{
	Broodwar->printf("%s left the game.",player->getName().c_str());
}
void ExampleAIModule::onNukeDetect(BWAPI::Position target)
{
	if (target!=Positions::Unknown)
	{
		Broodwar->printf("Nuclear Launch Detected at (%d,%d)",target.x(),target.y());
		Broodwar->pingMinimap(target);
	}
	else
		Broodwar->printf("Nuclear Launch Detected.  Fortunately, it doesn't seem to have been pointed at us.");
	Broodwar->sendText("NUKE!");
}

bool ExampleAIModule::onSendText(std::string text)
{
	if (!enhancedUI->onSendText(text)) {
		return false;
	} else if (!aiCommands(text)) {
		return false;
	} else if (!CFormation::onSendText(text)) {
		return false;
	} else if (text.substr(0,1) == "/")
	{
		Broodwar->printf("Command not recognized");
		return false;
	}
	return true;
}

bool ExampleAIModule::aiCommands(std::string text)
{
	UnitType unit = UnitTypes::getUnitType(text);
	TechType tech = TechTypes::getTechType(text);
	UpgradeType upgrade = UpgradeTypes::getUpgradeType(text);
	if (text=="/debug orders")
	{
		this->buildOrderManager->toggleDebugMode();
		return false;
	} else if (text=="/debug scout")
	{
		this->scoutManager->toggleDebugMode();
		return false;
	} else if (text=="/expand")
	{
		this->baseManager->expand();
		return false;
	} else if (unit != UnitTypes::Unknown)
	{
		this->buildOrderManager->buildAdditional(1,unit,300);
		return false;
	} else if (tech != TechTypes::Unknown)
	{
		this->techManager->research(tech);
		return false;
	} else if (upgrade != UpgradeTypes::Unknown)
	{
		this->upgradeManager->upgrade(upgrade);
		return false;
	}
	return true;
}

DWORD WINAPI AnalyzeThread()
{
	BWTA::analyze();
	analyzed = true;
	analysis_just_finished = true;
	return 0;
}
